<!DOCTYPE html>
<html>
  <head><meta name="generator" content="Hexo 3.8.0">
  <meta http-equiv="content-type" content="text/html; charset=utf-8">
  <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport">
  <meta name="description" content="fengzhaoyang&#39;s blog">
  <meta name="keyword" content="hexo-theme, vuejs">
  
  <link rel="shortcut icon" href="/css/images/logo.png">
  
  <title>
    
    典型数据库架构设计与实践 | fzy-blog
    
  </title>
  <link href="//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
  <link href="//cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.css" rel="stylesheet">
  <link href="//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/tomorrow.min.css" rel="stylesheet">
  <link rel="stylesheet" href="/css/style.css">
  
  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/geopattern/1.2.3/js/geopattern.min.js"></script>
  <script src="//cdnjs.cloudflare.com/ajax/libs/nprogress/0.2.0/nprogress.min.js"></script>
  
  <script src="/js/qrious.js"></script>
  
  
  
  
    <!-- MathJax support START -->
    <script type="text/x-mathjax-config">
      MathJax.Hub.Config({
        tex2jax: {
          inlineMath: [ ['$','$'], ["\\(","\\)"]  ],
          processEscapes: true,
          skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
        }
      });
    </script>

    <script type="text/x-mathjax-config">
      MathJax.Hub.Queue(function() {
        var all = MathJax.Hub.getAllJax(), i;
        for (i=0; i < all.length; i += 1) {
          all[i].SourceElement().parentNode.className += ' has-jax';
        }
      });
    </script>
    <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
    <!-- MathJax support END -->
  


</head>
<div class="wechat-share">
  <img src="/css/images/logo.png">
</div>
  <body>
    <header class="header fixed-header">
  <div class="header-container">
    <a class="home-link" href="/">
      <div class="logo"></div>
      <span>fzy-blog</span>
    </a>
    <ul class="right-list">
      
        <li class="list-item">
          
            <a href="/" class="item-link">Home</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/tags/" class="item-link">Tags</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/archives/" class="item-link">Archives</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/project/" class="item-link">Projects</a>
          
        </li>
      
        <li class="list-item">
          
            <a href="/about/" class="item-link">About</a>
          
        </li>
      
    </ul>
    <div class="menu">
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
      <span class="icon-bar"></span>
    </div>
    <div class="menu-mask">
      <ul class="menu-list">
        
          <li class="menu-item">
            
              <a href="/" class="menu-link">Home</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/tags/" class="menu-link">Tags</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/archives/" class="menu-link">Archives</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/project/" class="menu-link">Projects</a>
            
          </li>
        
          <li class="menu-item">
            
              <a href="/about/" class="menu-link">About</a>
            
          </li>
        
      </ul>
    </div>
  </div>
</header>

    <div id="article-banner">
  <h2>典型数据库架构设计与实践</h2>
  <p class="post-date">2019-05-24</p>
  <div class="arrow-down">
    <a href="javascript:;"></a>
  </div>
</div>
<main class="app-body flex-box">
  <!-- Article START -->
  <article class="post-article">
    <section class="markdown-content"><p>本文，将介绍数据库架构设计中的一些基本概念，常见问题以及对应解决方案，为了便于读者理解，将以“用户中心”数据库为例，讲解数据库架构设计的常见玩法。</p>
<p>一、用户中心</p>
<p>用户中心是一个常见业务，主要提供用户注册、登录、信息查询与修改的服务，其核心元数据为：</p>
<p>User(uid, uname, passwd, sex, age,nickname, …)</p>
<p>其中：</p>
<p>uid 为用户 ID，主键</p>
<p>uname, passwd, sex, age, nickname, …等为用户的属性</p>
<p>数据库设计上，一般来说在业务初期，单库单表就能够搞定这个需求。</p>
<p>二、图示说明</p>
<p>为了方便大家理解，后文图片说明较多，其中：</p>
<p>“灰色”方框，表示 service，服务</p>
<p>“紫色”圆框，标识 master，主库</p>
<p>“粉色”圆框，表示 slave，从库</p>
<p>三、单库架构</p>
<p>最常见的架构设计如上：</p>
<p>user-service：用户中心服务，对调用者提供友好的 RPC 接口</p>
<p>user-db：一个库进行数据存储</p>
<p>四、分组架构</p>
<p>什么是分组？</p>
<p>答：分组架构是最常见的一主多从，主从同步，读写分离数据库架构：</p>
<p>user-service：依旧是用户中心服务</p>
<p>user-db-M(master)：主库，提供数据库写服务</p>
<p>user-db-S(slave)：从库，提供数据库读服务</p>
<p>主和从构成的数据库集群称为“组”。</p>
<p>分组有什么特点？</p>
<p>答：同一个组里的数据库集群：</p>
<p>主从之间通过 binlog 进行数据同步</p>
<p>多个实例数据库结构完全相同</p>
<p>多个实例存储的数据也完全相同，本质上是将数据进行复制</p>
<p>分组架构究竟解决什么问题？</p>
<p>答：大部分互联网业务读多写少，数据库的读往往最先成为性能瓶颈，如果希望：</p>
<p>线性提升数据库读性能</p>
<p>通过消除读写锁冲突提升数据库写性能</p>
<p>通过冗余从库实现数据的“读高可用”</p>
<p>此时可以使用分组架构，需要注意的是，分组架构中，数据库的主库依然是写单点。</p>
<p>一句话总结，分组解决的是“数据库读写高并发量高”问题，所实施的架构设计。</p>
<p>五、分片架构</p>
<p>什么是分片？</p>
<p>答：分片架构是大伙常说的水平切分(sharding)数据库架构：</p>
<p>user-service：依旧是用户中心服务</p>
<p>user-db1：水平切分成 2 份中的第一份</p>
<p>user-db2：水平切分成 2 份中的第二份</p>
<p>分片后，多个数据库实例也会构成一个数据库集群。</p>
<p>水平切分，到底是分库还是分表？</p>
<p>答：强烈建议分库，而不是分表，因为：</p>
<p>分表依然公用一个数据库文件，仍然有磁盘 IO 的竞争</p>
<p>分库能够很容易的将数据迁移到不同数据库实例，甚至数据库机器上，扩展性更好</p>
<p>水平切分，用什么算法？</p>
<p>答：常见的水平切分算法有“范围法”和“哈希法”：</p>
<p>范围法如上图：以用户中心的业务主键 uid 为划分依据，将数据水平切分到两个数据库实例上去：</p>
<p>user-db1：存储 0 到 1 千万的 uid 数据</p>
<p>user-db2：存储 0 到 2 千万的 uid 数据</p>
<p>哈希法如上图：也是以用户中心的业务主键 uid 为划分依据，将数据水平切分到两个数据库实例上去：</p>
<p>user-db1：存储 uid 取模得 1 的 uid 数据</p>
<p>user-db2：存储 uid 取模得 0 的 uid 数据</p>
<p>这两种方法在互联网都有使用，其中哈希法使用较为广泛。</p>
<p>分片有什么特点？</p>
<p>答：同一个分片里的数据库集群：</p>
<p>多个实例之间本身不直接产生联系，不像主从间有 binlog 同步</p>
<p>多个实例数据库结构，也完全相同</p>
<p>多个实例存储的数据之间没有交集，所有实例间数据并集构成全局数据</p>
<p>分片架构究竟解决什么问题？</p>
<p>答：大部分互联网业务数据量很大，单库容量容易成为瓶颈，此时通过分片可以：</p>
<p>线性提升数据库写性能，需要注意的是，分组架构是不能线性提升数据库写性能的</p>
<p>降低单库数据容量</p>
<p>一句话总结，分片解决的是“数据库数据量大”问题，所实施的架构设计。</p>
<p>六、分组+分片架构</p>
<p>如果业务读写并发量很高，数据量也很大，通常需要实施分组+分片的数据库架构：</p>
<p>通过分片来降低单库的数据量，线性提升数据库的写性能</p>
<p>通过分组来线性提升数据库的读性能，保证读库的高可用</p>
<p>七、垂直切分</p>
<p>除了水平切分，垂直切分也是一类常见的数据库架构设计，垂直切分一般和业务结合比较紧密。</p>
<p>还是以用户中心为例，可以这么进行垂直切分：</p>
<p>User(uid, uname, passwd, sex, age, …)</p>
<p>User_EX(uid, intro, sign, …)</p>
<p>垂直切分开的表，主键都是 uid</p>
<p>登录名，密码，性别，年龄等属性放在一个垂直表（库）里</p>
<p>自我介绍，个人签名等属性放在另一个垂直表（库）里</p>
<p>如何进行垂直切分？</p>
<p>答：根据业务对数据进行垂直切分时，一般要考虑属性的“长度”和“访问频度”两个因素：</p>
<p>长度较短，访问频率较高的放在一起</p>
<p>长度较长，访问频度较低的放在一起</p>
<p>这是因为，数据库会以行(row)为单位，将数 load 到内存(buffer)里，在内存容量有限的情况下，长度短且访问频度高的属性，内存能够 load 更多的数据，命中率会更高，磁盘 IO 会减少，数据库的性能会提升。</p>
<p>垂直切分有什么特点？</p>
<p>答：垂直切分和水平切有相似的地方，又不太相同：</p>
<p>多个实例之间也不直接产生联系，即没有 binlog 同步</p>
<p>多个实例数据库结构，都不一样</p>
<p>多个实例存储的数据之间至少有一列交集，一般来说是业务主键，所有实例间数据并集构成全局数据</p>
<p>垂直切分解决什么问题？</p>
<p>答：垂直切分即可以降低单库的数据量，还可以降低磁盘 IO 从而提升吞吐量，但它与业务结合比较紧密，并不是所有业务都能够进行垂直切分的。</p>
<p>八、总结</p>
<p>文章较长，希望至少记住这么几点：</p>
<p>业务初期用单库</p>
<p>读压力大，读高可用，用分组</p>
<p>数据量大，写线性扩容，用分片</p>
<p>属性短，访问频度高的属性，垂直拆分到一起</p>
<p>来源： <a href="https://mp.weixin.qq.com/s/qbVrQ_aUdL9k28XVinpJXA" target="_blank" rel="noopener">https://mp.weixin.qq.com/s/qbVrQ_aUdL9k28XVinpJXA</a></p>
</section>
    <!-- Tags START -->
    
    <div class="tags">
      <span>Tags:</span>
      
  <a href="/tags#分布式微服务架构">
    <span class="tag-code">分布式微服务架构</span>
  </a>

    </div>
    
    <!-- Tags END -->
    <!-- NAV START -->
    
  <div class="nav-container">
    <!-- reverse left and right to put prev and next in a more logic postition -->
    
      <a class="nav-left" href="/2019/05/24/分布式微服务架构/各大互联网公司架构演进之路汇总/">
        <span class="nav-arrow">← </span>
        
          各大互联网公司架构演进之路汇总
        
      </a>
    
    
      <a class="nav-right" href="/2019/05/24/分布式微服务架构/分布式系统-基本原理及高可用策略/">
        
          分布式系统-基本原理及高可用策略
        
        <span class="nav-arrow"> →</span>
      </a>
    
  </div>

    <!-- NAV END -->
    <!-- 打赏 START -->
    
    <div class="money-like">
      <div class="reward-btn">
        赏
        <span class="money-code">
          <span class="alipay-code">
            <div class="code-image"></div>
            <b>使用支付宝打赏</b>
          </span>
          <span class="wechat-code">
            <div class="code-image"></div>
            <b>使用微信打赏</b>
          </span>
        </span>
      </div>
      <p class="notice">若你觉得我的文章对你有帮助，欢迎点击上方按钮对我打赏</p>
    </div>
    
    <!-- 打赏 END -->
    <!-- 二维码 START -->
    
    <div class="qrcode">
      <canvas id="share-qrcode"></canvas>
      <p class="notice">扫描二维码，分享此文章</p>
    </div>
    
    <!-- 二维码 END -->
    
    <!-- No Comment -->
    
  </article>
  <!-- Article END -->
  <!-- Catalog START -->
  
  <aside class="catalog-container">
  <div class="toc-main">
    <strong class="toc-title">Catalog</strong>
    
      <ol class="nav">none</ol>
    
  </div>
</aside>
  
  <!-- Catalog END -->
</main>

<script>
  (function () {
    var url = 'https://fengzhaoy.github.io/2019/05/24/分布式微服务架构/典型数据库架构设计与实践/';
    var banner = ''
    if (banner !== '' && banner !== 'undefined' && banner !== 'null') {
      $('#article-banner').css({
        'background-image': 'url(' + banner + ')'
      })
    } else {
      $('#article-banner').geopattern(url)
    }
    $('.header').removeClass('fixed-header')

    // error image
    $(".markdown-content img").on('error', function () {
      $(this).attr('src', 'http://file.muyutech.com/error-img.png')
      $(this).css({
        'cursor': 'default'
      })
    })

    // zoom image
    $(".markdown-content img").on('click', function () {
      var src = $(this).attr('src')
      if (src !== 'http://file.muyutech.com/error-img.png') {
        var imageW = $(this).width()
        var imageH = $(this).height()

        var zoom = ($(window).width() * 0.95 / imageW).toFixed(2)
        zoom = zoom < 1 ? 1 : zoom
        zoom = zoom > 2 ? 2 : zoom
        var transY = (($(window).height() - imageH) / 2).toFixed(2)

        $('body').append('<div class="image-view-wrap"><div class="image-view-inner"><img src="' + src +
          '" /></div></div>')
        $('.image-view-wrap').addClass('wrap-active')
        $('.image-view-wrap img').css({
          'width': `${imageW}`,
          'transform': `translate3d(0, ${transY}px, 0) scale3d(${zoom}, ${zoom}, 1)`
        })
        $('html').css('overflow', 'hidden')

        $('.image-view-wrap').on('click', function () {
          $(this).remove()
          $('html').attr('style', '')
        })
      }
    })
  })();
</script>


<script>
  var qr = new QRious({
    element: document.getElementById('share-qrcode'),
    value: document.location.href
  });
</script>





    <div class="scroll-top">
  <span class="arrow-icon"></span>
</div>
    <footer class="app-footer">
  <p class="copyright">
    &copy; 2019
  </p>
</footer>

<script>
  function async (u, c) {
    var d = document,
      t = 'script',
      o = d.createElement(t),
      s = d.getElementsByTagName(t)[0];
    o.src = u;
    if (c) {
      o.addEventListener('load', function (e) {
        c(null, e);
      }, false);
    }
    s.parentNode.insertBefore(o, s);
  }
</script>
<script>
  async ("//cdnjs.cloudflare.com/ajax/libs/fastclick/1.0.6/fastclick.min.js", function () {
    FastClick.attach(document.body);
  })
</script>

<script>
  var hasLine = 'true';
  async ("//cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js", function () {
    $('figure pre').each(function (i, block) {
      var figure = $(this).parents('figure');
      if (hasLine === 'false') {
        figure.find('.gutter').hide();
      }
      var lang = figure.attr('class').split(' ')[1] || 'code';
      var codeHtml = $(this).html();
      var codeTag = document.createElement('code');
      codeTag.className = lang;
      codeTag.innerHTML = codeHtml;
      $(this).attr('class', '').empty().html(codeTag);
      figure.attr('data-lang', lang.toUpperCase());
      hljs.highlightBlock(block);
    });
  })
</script>
<!-- Baidu Tongji -->

<script src="/js/script.js"></script>
  </body>
</html>